#include <string.h>
#include <iostream>
#include <vector>
using std::cout;
using std::cin;
using std::vector;
using std::endl;

class String
{
public:
	String()
    :_pstr(new char[1]())
    {

    }
	String(const char * pstr)
    :_pstr(new char[strlen(pstr) + 1]())
    {
        strcpy(_pstr, pstr);
    }

	String(const String & rhs)
    :_pstr(new char[strlen(rhs._pstr) + 1]())
    { 
        strcpy(_pstr, rhs._pstr);
    }
    ~String(){
        if(_pstr){
            delete [] _pstr;
            _pstr = nullptr;
        }
    }
    //四步走策略：
    String &operator=(const String & rhs){
        if(this != &rhs){
            //回收左操作数申请的空间
            delete [] _pstr;
            //执行深拷贝操作
            _pstr = new char[strlen(rhs._pstr) + 1]();
            strcpy(_pstr, rhs._pstr);
        }
        return *this;
    }

    String &operator=(const char * pstr){
        String temp(pstr);
        //实际这一步调用的是上述的重载函数形式
        *this = temp;
        return *this;
    }

    // s1 += s2;
    // 分析：
    // 1.先申请一个新的临时空间，大小为s1 + s2的大小
    // 2.把s1和s2的内容复制到新的临时空间中
    // 3.回收左操作数s1原先的空间
    // 4.指针指向新的临时空间
    // 5.返回当前对象
    String &operator+=(const String & rhs){
        char * temp = new char[strlen(_pstr) + strlen(rhs._pstr) + 1]();
        strcpy(temp, _pstr);
        strcat(temp, rhs._pstr);
        delete [] _pstr;
        _pstr = temp;
        return *this;
    }

    String &operator+=(const char * pstr){
        String temp(pstr);
        //也是利用上面的重载函数来实现的
        *this += temp;
        return *this;
    }

    //允许修改的
    //为什么要有两种不同的函数形式存在呢？？？？？
    //一个是可以修改，一个是只能够进行只读操作
    //const对象能够调用下面的函数
    char &operator[](std::size_t index){
        if(index >= size()){
            static char nullchar = '\0';
            return nullchar;
        }
        return _pstr[index];
    }
    //只读的
    const char &operator[](std::size_t index) const{
        if(index >= size()){
            static char nullchar = '\0';
            return nullchar;
        }
        return _pstr[index];
    }

    std::size_t size() const{
        return strlen(_pstr);
    }
    const char* c_str() const{
        return _pstr;
    }

	friend bool operator==(const String &, const String &);
	friend bool operator!=(const String &, const String &);

	friend bool operator<(const String &, const String &);
	friend bool operator>(const String &, const String &);
	friend bool operator<=(const String &, const String &);
	friend bool operator>=(const String &, const String &);

	friend std::ostream &operator<<(std::ostream &os, const String &s);
	friend std::istream &operator>>(std::istream &is, String &s);

private:
	char * _pstr;
};
//以下三个函数全部利用了函数重载来进行实现
String operator+(const String & lhs, const String & rhs){
    //调用的是拷贝构造函数
    String temp(lhs);
    //前面的+=运算符
    temp += rhs;
    return temp;
}
String operator+(const String & lhs, const char * pstr){
    String temp(lhs);
    temp += pstr;
    return temp;
}
String operator+(const char * pstr, const String & rhs){
    //调用的是构造函数
    String temp(pstr);
    temp += rhs;
    return temp;
}
bool operator==(const String & lhs, const String & rhs){
    return strcmp(lhs._pstr, rhs._pstr) == 0;
}

std::ostream &operator<<(std::ostream &os, const String &s){
    os << s._pstr;
    return os;
}
//应当考虑的点：
//1.不可以直接使用is >> s._pstr，指针变量使用之前必须得进行初始化
//2.如果之前的s._pstr指向了某个堆空间，那么还需要考虑是否会有内存泄漏
//流程：
//首先先判断当前的指针是否指向了有效的位置，
//如果有则回收该空间
//使用vector来去接收用户录入的字符数据(不确定用户究竟录入了多少个字符)
//申请堆空间，将vector里面的字符复制到堆空间中
//指针再去指向该堆空间
std::istream &operator>>(std::istream &is, String &s){
    //is >> s._pstr;
    //可以以换行符作为用户录入数据结束的一个标志
    if(s._pstr){
        delete [] s._pstr;
    }
    vector<char> _input;
    //定义一个char类型的变量
    char c;
    //每次从is输入流中提取一个字符，赋值给char变量
    //判断当前这个变量是否是换行符
    //如果不是换行符，则放入vector中
    while((c = is.get()) != '\n'){
        _input.push_back(c);
    }
    //统计vector的长度
    char * temp = new char[_input.size() + 1]();
    for(size_t i = 0; i < _input.size(); ++i){
        temp[i] = _input[i];
    }

    //指针去指向该堆空间
    s._pstr = temp;
    return is;
}

void test(){
    String s;
    String s1 = "hello";
    cout << s1 << endl;
    String s2 = s1;
    cout << s2 << endl;
    String s3 = "world";
    cout << s3 << endl;
    s3 = s1;
    cout << s3 << endl;
}

void test2(){
    String s1("hello");
    s1 = "world";
    cout << s1 << endl;
    cout << "===========" << endl;
    String s2 = "world";
    s1 += s2;
    cout << s1 << endl;
    cout << "=============" << endl;
    String s3 = "hello,world";
    s3 += s3;
    cout << s3 << endl;
}

void test3(){
    const String s1("hello,world");
    //s1 = "hello";
    //为什么要有两个下标访问运算符的含义
    //为了让const对象也可以调用下标
    cout << s1[0] << endl;
}

void test4(){
    String s;
    //输入流运算符重载
    cin >> s;
    cout << s << endl;
}

int main()
{
    test4();
    return 0;
}

